package com.yang.simple_rpc.client;

import com.yang.simple_rpc.client.discovery.ServiceDiscovery;
import com.yang.simple_rpc.client.net.NetClient;
import com.yang.simple_rpc.common.protocol.MessageProtocol;
import com.yang.simple_rpc.common.protocol.SimpleRequest;
import com.yang.simple_rpc.common.protocol.SimpleResponse;
import com.yang.simple_rpc.common.service.Service;
import com.yang.simple_rpc.exception.SimpleException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import static java.lang.reflect.Proxy.newProxyInstance;

/**
 * @author : morikawaaaa
 * @date : 2022
 */
public class ClientProxyFactory {
    private ServiceDiscovery serviceDiscovery;
    private Map<String, MessageProtocol> supportMessageProtocol;
    private NetClient netClient;
    private Map<Class<?>, Object> objectCache = new HashMap<>();

    @SuppressWarnings("Unchecked")
    public <T> T getProxy(Class <T> clazz){
        return (T) this.objectCache.computeIfAbsent(clazz, cls -> newProxyInstance(cls.getClassLoader(),
                new Class<?>[]{cls}, new ClientInvocationHandler(cls)));
    }

    public ServiceDiscovery getServiceDiscovery(){
        return serviceDiscovery;
    }

    public void setSid(ServiceDiscovery sd){
        this.serviceDiscovery = sd;
    }

    public Map<String , MessageProtocol> getSupportMessageProtocol(){
        return supportMessageProtocol;
    }

    public void setSupportMessageProtocol(Map<String, MessageProtocol> supportMessageProtocol){
        this.supportMessageProtocol = supportMessageProtocol;
    }

    public NetClient getNetClient(){
        return netClient;
    }

    public void setNetClient(NetClient nc){
        this.netClient = nc;
    }



    private class ClientInvocationHandler implements InvocationHandler{
        private Class<?> clazz;
        private Random random = new Random();
        public ClientInvocationHandler(Class<?> clazz){
            super();
            this.clazz = clazz;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
            if(method.getName().equals("toString")){
                return proxy.getClass().toString();
            }

            if(method.getName().equals("hashCode")){
                return proxy.getClass().hashCode();
            }

            //获得服务信息
            String serviceName = this.clazz.getName();
            List<Service> services = serviceDiscovery.getService(serviceName);

            if(services == null || services.isEmpty()){
                throw new SimpleException("No provider available");
            }

            //随机选择一个服务提供者（使用随机数实现软负载均衡）
            Service service = services.get(random.nextInt(services.size()));

            //构造request对象
            SimpleRequest sreq = new SimpleRequest();
            sreq.setServiceName(service.getName());
            sreq.setMethod(method.getName());
            sreq.setParameterTypes(method.getParameterTypes());
            sreq.setParameters(args);

            //协议层编组，获得该方法对应的协议
            MessageProtocol protocol = supportMessageProtocol.get(service.getProtocol());
            byte[] data = protocol.marshallingRequest(sreq);
            //调用网络层发送请求
            byte[] repData = netClient.sendRequest(data, service);
            //解组响应消息
            SimpleResponse sresp = protocol.unmarshallingReponse(repData);
            //结果处理
            if(sresp.getException() != null){
                throw sresp.getException();
            }
            return sresp.getReturnValue();
        }
    }

}
